// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build ignore
// +build ignore

package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/format"
	"io/ioutil"
	"log"
	"os"
)

var debug = flag.Bool("debug", false, "")

func main() {
	flag.Parse()

	// Generate table.go.
	{
		w := &bytes.Buffer{}
		w.WriteString(header)
		w.WriteString(decodeHeaderComment)
		writeDecodeTable(w, build(modeCodes[:], 0), "modeDecodeTable",
			"// modeDecodeTable represents Table 1 and the End-of-Line code.\n")
		writeDecodeTable(w, build(whiteCodes[:], 0), "whiteDecodeTable",
			"// whiteDecodeTable represents Tables 2 and 3 for a white run.\n")
		writeDecodeTable(w, build(blackCodes[:], 0), "blackDecodeTable",
			"// blackDecodeTable represents Tables 2 and 3 for a black run.\n")
		writeMaxCodeLength(w, modeCodes[:], whiteCodes[:], blackCodes[:])
		w.WriteString(encodeHeaderComment)
		w.WriteString(bitStringTypeDef)
		writeEncodeTable(w, modeCodes[:], "modeEncodeTable",
			"// modeEncodeTable represents Table 1 and the End-of-Line code.\n")
		writeEncodeTable(w, whiteCodes[:64], "whiteEncodeTable2",
			"// whiteEncodeTable2 represents Table 2 for a white run.\n")
		writeEncodeTable(w, whiteCodes[64:], "whiteEncodeTable3",
			"// whiteEncodeTable3 represents Table 3 for a white run.\n")
		writeEncodeTable(w, blackCodes[:64], "blackEncodeTable2",
			"// blackEncodeTable2 represents Table 2 for a black run.\n")
		writeEncodeTable(w, blackCodes[64:], "blackEncodeTable3",
			"// blackEncodeTable3 represents Table 3 for a black run.\n")
		finish(w, "table.go")
	}

	// Generate table_test.go.
	{
		w := &bytes.Buffer{}
		w.WriteString(header)
		finish(w, "table_test.go")
	}
}

const header = `// generated by "go run gen.go". DO NOT EDIT.

package ccitt

`

const decodeHeaderComment = `
// Each decodeTable is represented by an array of [2]int16's: a binary tree.
// Each array element (other than element 0, which means invalid) is a branch
// node in that tree. The root node is always element 1 (the second element).
//
// To walk the tree, look at the next bit in the bit stream, using it to select
// the first or second element of the [2]int16. If that int16 is 0, we have an
// invalid code. If it is positive, go to that branch node. If it is negative,
// then we have a leaf node, whose value is the bitwise complement (the ^
// operator) of that int16.
//
// Comments above each decodeTable also show the same structure visually. The
// "b123" lines show the 123'rd branch node. The "=XXXXX" lines show an invalid
// code. The "=v1234" lines show a leaf node with value 1234. When reading the
// bit stream, a 0 or 1 bit means to go up or down, as you move left to right.
//
// For example, in modeDecodeTable, branch node b005 is three steps up from the
// root node, meaning that we have already seen "000". If the next bit is "0"
// then we move to branch node b006. Otherwise, the next bit is "1", and we
// move to the leaf node v0000 (also known as the modePass constant). Indeed,
// the bits that encode modePass are "0001".
//
// Tables 1, 2 and 3 come from the "ITU-T Recommendation T.6: FACSIMILE CODING
// SCHEMES AND CODING CONTROL FUNCTIONS FOR GROUP 4 FACSIMILE APPARATUS"
// specification:
//
// https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.6-198811-I!!PDF-E&type=items


`

const encodeHeaderComment = `
// Each encodeTable is represented by an array of bitStrings.


`

type node struct {
	children    [2]*node
	val         uint32
	branchIndex int32
}

func (n *node) isBranch() bool {
	return (n != nil) && ((n.children[0] != nil) || (n.children[1] != nil))
}

func (n *node) String() string {
	if n == nil {
		return "0"
	}
	if n.branchIndex > 0 {
		return fmt.Sprintf("%d", n.branchIndex)
	}
	return fmt.Sprintf("^%d", n.val)
}

func build(codes []code, prefixLen int) *node {
	if len(codes) == 0 {
		return nil
	}

	if prefixLen == len(codes[0].str) {
		if len(codes) != 1 {
			panic("ambiguous codes")
		}
		return &node{
			val: codes[0].val,
		}
	}

	childrenCodes := [2][]code{}
	for _, code := range codes {
		bit := code.str[prefixLen] & 1
		childrenCodes[bit] = append(childrenCodes[bit], code)
	}
	return &node{
		children: [2]*node{
			build(childrenCodes[0], prefixLen+1),
			build(childrenCodes[1], prefixLen+1),
		},
	}
}

func writeDecodeTable(w *bytes.Buffer, root *node, varName, comment string) {
	assignBranchIndexes(root)

	w.WriteString(comment)
	w.WriteString("//\n")
	writeComment(w, root, "  ", false)
	fmt.Fprintf(w, "var %s = [...][2]int16{\n", varName)
	fmt.Fprintf(w, "0: {0, 0},\n")

	// Walk the tree in breadth-first order.
	for queue := []*node{root}; len(queue) > 0; {
		n := queue[0]
		queue = queue[1:]

		if n.isBranch() {
			fmt.Fprintf(w, "%d: {%v, %v},\n", n.branchIndex, n.children[0], n.children[1])
			queue = append(queue, n.children[0], n.children[1])
		}
	}

	fmt.Fprintf(w, "}\n\n")
}

const bitStringTypeDef = `
// bitString is a pair of uint32 values representing a bit code.
// The nBits low bits of bits make up the actual bit code.
// Eg. bitString{0x0004, 8} represents the bitcode "00000100".
type bitString struct {
	bits  uint32
	nBits uint32
}

`

func writeEncodeTable(w *bytes.Buffer, codes []code, varName, comment string) {
	w.WriteString(comment)
	fmt.Fprintf(w, "var %s = [...]bitString{\n", varName)
	for i, code := range codes {
		s := code.str
		n := uint32(len(s))
		c := uint32(0)
		for j := uint32(0); j < n; j++ {
			c |= uint32(s[j]&1) << (n - j - 1)
		}
		fmt.Fprintf(w, "%d: {0x%04x, %v}, // %q \n", i, c, n, s)
	}
	fmt.Fprintf(w, "}\n\n")
}

func assignBranchIndexes(root *node) {
	// 0 is reserved for an invalid value.
	branchIndex := int32(1)

	// Walk the tree in breadth-first order.
	for queue := []*node{root}; len(queue) > 0; {
		n := queue[0]
		queue = queue[1:]

		if n.isBranch() {
			n.branchIndex = branchIndex
			branchIndex++
			queue = append(queue, n.children[0], n.children[1])
		}
	}
}

func writeComment(w *bytes.Buffer, n *node, prefix string, down bool) {
	if n.isBranch() {
		prefixUp := prefix[:len(prefix)-2] + "  | "
		prefixDown := prefix + "| "
		if down {
			prefixUp, prefixDown = prefixDown, prefixUp
		}

		writeComment(w, n.children[0], prefixUp, false)
		defer writeComment(w, n.children[1], prefixDown, true)

		fmt.Fprintf(w, "// b%03d ", n.branchIndex)
	} else {
		fmt.Fprintf(w, "//      ")
	}

	w.WriteString(prefix[:len(prefix)-2])

	if n == nil {
		fmt.Fprintf(w, "+=XXXXX\n")
		return
	}
	if !n.isBranch() {
		fmt.Fprintf(w, "+=v%04d\n", n.val)
		return
	}
	w.WriteString("+-+\n")
}

func writeMaxCodeLength(w *bytes.Buffer, codesList ...[]code) {
	maxCodeLength := 0
	for _, codes := range codesList {
		for _, code := range codes {
			if n := len(code.str); maxCodeLength < n {
				maxCodeLength = n
			}
		}
	}
	fmt.Fprintf(w, "const maxCodeLength = %d\n\n", maxCodeLength)
}

func finish(w *bytes.Buffer, filename string) {
	copyPaste(w, filename)
	if *debug {
		os.Stdout.Write(w.Bytes())
		return
	}
	out, err := format.Source(w.Bytes())
	if err != nil {
		log.Fatalf("format.Source: %v", err)
	}
	if err := ioutil.WriteFile(filename, out, 0660); err != nil {
		log.Fatalf("ioutil.WriteFile: %v", err)
	}
}

func copyPaste(w *bytes.Buffer, filename string) {
	b, err := ioutil.ReadFile("gen.go")
	if err != nil {
		log.Fatalf("ioutil.ReadFile: %v", err)
	}
	begin := []byte("\n// COPY PASTE " + filename + " BEGIN\n\n")
	end := []byte("\n// COPY PASTE " + filename + " END\n\n")

	for len(b) > 0 {
		i := bytes.Index(b, begin)
		if i < 0 {
			break
		}
		b = b[i:]

		j := bytes.Index(b, end)
		if j < 0 {
			break
		}
		j += len(end)

		w.Write(b[:j])
		b = b[j:]
	}
}

// COPY PASTE table.go BEGIN

const (
	modePass = iota // Pass
	modeH           // Horizontal
	modeV0          // Vertical-0
	modeVR1         // Vertical-Right-1
	modeVR2         // Vertical-Right-2
	modeVR3         // Vertical-Right-3
	modeVL1         // Vertical-Left-1
	modeVL2         // Vertical-Left-2
	modeVL3         // Vertical-Left-3
	modeExt         // Extension
)

// COPY PASTE table.go END

// The data that is the rest of this file is taken from Tables 1, 2 and 3 from
// the "ITU-T Recommendation T.6" spec.

// COPY PASTE table_test.go BEGIN

type code struct {
	val uint32
	str string
}

var modeCodes = []code{
	{modePass, "0001"},
	{modeH, "001"},
	{modeV0, "1"},
	{modeVR1, "011"},
	{modeVR2, "000011"},
	{modeVR3, "0000011"},
	{modeVL1, "010"},
	{modeVL2, "000010"},
	{modeVL3, "0000010"},
	{modeExt, "0000001"},
}

var whiteCodes = []code{
	// Terminating codes (0-63).
	{0x0000, "00110101"},
	{0x0001, "000111"},
	{0x0002, "0111"},
	{0x0003, "1000"},
	{0x0004, "1011"},
	{0x0005, "1100"},
	{0x0006, "1110"},
	{0x0007, "1111"},
	{0x0008, "10011"},
	{0x0009, "10100"},
	{0x000A, "00111"},
	{0x000B, "01000"},
	{0x000C, "001000"},
	{0x000D, "000011"},
	{0x000E, "110100"},
	{0x000F, "110101"},
	{0x0010, "101010"},
	{0x0011, "101011"},
	{0x0012, "0100111"},
	{0x0013, "0001100"},
	{0x0014, "0001000"},
	{0x0015, "0010111"},
	{0x0016, "0000011"},
	{0x0017, "0000100"},
	{0x0018, "0101000"},
	{0x0019, "0101011"},
	{0x001A, "0010011"},
	{0x001B, "0100100"},
	{0x001C, "0011000"},
	{0x001D, "00000010"},
	{0x001E, "00000011"},
	{0x001F, "00011010"},
	{0x0020, "00011011"},
	{0x0021, "00010010"},
	{0x0022, "00010011"},
	{0x0023, "00010100"},
	{0x0024, "00010101"},
	{0x0025, "00010110"},
	{0x0026, "00010111"},
	{0x0027, "00101000"},
	{0x0028, "00101001"},
	{0x0029, "00101010"},
	{0x002A, "00101011"},
	{0x002B, "00101100"},
	{0x002C, "00101101"},
	{0x002D, "00000100"},
	{0x002E, "00000101"},
	{0x002F, "00001010"},
	{0x0030, "00001011"},
	{0x0031, "01010010"},
	{0x0032, "01010011"},
	{0x0033, "01010100"},
	{0x0034, "01010101"},
	{0x0035, "00100100"},
	{0x0036, "00100101"},
	{0x0037, "01011000"},
	{0x0038, "01011001"},
	{0x0039, "01011010"},
	{0x003A, "01011011"},
	{0x003B, "01001010"},
	{0x003C, "01001011"},
	{0x003D, "00110010"},
	{0x003E, "00110011"},
	{0x003F, "00110100"},

	// Make-up codes between 64 and 1728.
	{0x0040, "11011"},
	{0x0080, "10010"},
	{0x00C0, "010111"},
	{0x0100, "0110111"},
	{0x0140, "00110110"},
	{0x0180, "00110111"},
	{0x01C0, "01100100"},
	{0x0200, "01100101"},
	{0x0240, "01101000"},
	{0x0280, "01100111"},
	{0x02C0, "011001100"},
	{0x0300, "011001101"},
	{0x0340, "011010010"},
	{0x0380, "011010011"},
	{0x03C0, "011010100"},
	{0x0400, "011010101"},
	{0x0440, "011010110"},
	{0x0480, "011010111"},
	{0x04C0, "011011000"},
	{0x0500, "011011001"},
	{0x0540, "011011010"},
	{0x0580, "011011011"},
	{0x05C0, "010011000"},
	{0x0600, "010011001"},
	{0x0640, "010011010"},
	{0x0680, "011000"},
	{0x06C0, "010011011"},

	// Make-up codes between 1792 and 2560.
	{0x0700, "00000001000"},
	{0x0740, "00000001100"},
	{0x0780, "00000001101"},
	{0x07C0, "000000010010"},
	{0x0800, "000000010011"},
	{0x0840, "000000010100"},
	{0x0880, "000000010101"},
	{0x08C0, "000000010110"},
	{0x0900, "000000010111"},
	{0x0940, "000000011100"},
	{0x0980, "000000011101"},
	{0x09C0, "000000011110"},
	{0x0A00, "000000011111"},
}

var blackCodes = []code{
	// Terminating codes (0-63).
	{0x0000, "0000110111"},
	{0x0001, "010"},
	{0x0002, "11"},
	{0x0003, "10"},
	{0x0004, "011"},
	{0x0005, "0011"},
	{0x0006, "0010"},
	{0x0007, "00011"},
	{0x0008, "000101"},
	{0x0009, "000100"},
	{0x000A, "0000100"},
	{0x000B, "0000101"},
	{0x000C, "0000111"},
	{0x000D, "00000100"},
	{0x000E, "00000111"},
	{0x000F, "000011000"},
	{0x0010, "0000010111"},
	{0x0011, "0000011000"},
	{0x0012, "0000001000"},
	{0x0013, "00001100111"},
	{0x0014, "00001101000"},
	{0x0015, "00001101100"},
	{0x0016, "00000110111"},
	{0x0017, "00000101000"},
	{0x0018, "00000010111"},
	{0x0019, "00000011000"},
	{0x001A, "000011001010"},
	{0x001B, "000011001011"},
	{0x001C, "000011001100"},
	{0x001D, "000011001101"},
	{0x001E, "000001101000"},
	{0x001F, "000001101001"},
	{0x0020, "000001101010"},
	{0x0021, "000001101011"},
	{0x0022, "000011010010"},
	{0x0023, "000011010011"},
	{0x0024, "000011010100"},
	{0x0025, "000011010101"},
	{0x0026, "000011010110"},
	{0x0027, "000011010111"},
	{0x0028, "000001101100"},
	{0x0029, "000001101101"},
	{0x002A, "000011011010"},
	{0x002B, "000011011011"},
	{0x002C, "000001010100"},
	{0x002D, "000001010101"},
	{0x002E, "000001010110"},
	{0x002F, "000001010111"},
	{0x0030, "000001100100"},
	{0x0031, "000001100101"},
	{0x0032, "000001010010"},
	{0x0033, "000001010011"},
	{0x0034, "000000100100"},
	{0x0035, "000000110111"},
	{0x0036, "000000111000"},
	{0x0037, "000000100111"},
	{0x0038, "000000101000"},
	{0x0039, "000001011000"},
	{0x003A, "000001011001"},
	{0x003B, "000000101011"},
	{0x003C, "000000101100"},
	{0x003D, "000001011010"},
	{0x003E, "000001100110"},
	{0x003F, "000001100111"},

	// Make-up codes between 64 and 1728.
	{0x0040, "0000001111"},
	{0x0080, "000011001000"},
	{0x00C0, "000011001001"},
	{0x0100, "000001011011"},
	{0x0140, "000000110011"},
	{0x0180, "000000110100"},
	{0x01C0, "000000110101"},
	{0x0200, "0000001101100"},
	{0x0240, "0000001101101"},
	{0x0280, "0000001001010"},
	{0x02C0, "0000001001011"},
	{0x0300, "0000001001100"},
	{0x0340, "0000001001101"},
	{0x0380, "0000001110010"},
	{0x03C0, "0000001110011"},
	{0x0400, "0000001110100"},
	{0x0440, "0000001110101"},
	{0x0480, "0000001110110"},
	{0x04C0, "0000001110111"},
	{0x0500, "0000001010010"},
	{0x0540, "0000001010011"},
	{0x0580, "0000001010100"},
	{0x05C0, "0000001010101"},
	{0x0600, "0000001011010"},
	{0x0640, "0000001011011"},
	{0x0680, "0000001100100"},
	{0x06C0, "0000001100101"},

	// Make-up codes between 1792 and 2560.
	{0x0700, "00000001000"},
	{0x0740, "00000001100"},
	{0x0780, "00000001101"},
	{0x07C0, "000000010010"},
	{0x0800, "000000010011"},
	{0x0840, "000000010100"},
	{0x0880, "000000010101"},
	{0x08C0, "000000010110"},
	{0x0900, "000000010111"},
	{0x0940, "000000011100"},
	{0x0980, "000000011101"},
	{0x09C0, "000000011110"},
	{0x0A00, "000000011111"},
}

// COPY PASTE table_test.go END

// This final comment makes the "END" above be followed by "\n\n".
